/*
 * Copyright (c) 2015. 石头哥哥
 */

package com.comom.manager.kit;

import java.security.SecureRandom;
import java.util.Random;

/**
 * 获取随机数工具
 * eg:Rnd.newInstance(RandomType.UNSECURE_VOLATILE).getInt(1,9)
 * --this method is best ,please see detail
 * Rnd.getInt(1,9)
 */
public final class Rnd {

    private final static long ADDEND = 0xBL;
    private final static long MASK = (1L << 48) - 1;
    private final static long MULTIPLIER = 0x5DEECE66DL;
    private static final RandomContainer rnd = newInstance(RandomType.UNSECURE_THREAD_LOCAL);
    private static volatile long SEED_UNIQUIFIER = 8682522807148012L;

    public static final Random directRandom() {
        return rnd.directRandom();
    }

    /**
     * 获取 double  值 ；0-nnumber from 0 to 1
     *
     * @return A random double number from 0 to 1
     * @see #nextDouble()
     */
    public static final double getDouble() {
        return rnd.nextDouble();
    }

    /**
     * 获取 integer  值 ；0-n
     *
     * @param n The superior limit (exclusive)
     * @return A random integer number from 0 to n-1
     */
    public static final int getInt(final int n) {
        return rnd.getInt(n);
    }

    /**
     * 获取区间随机值（min--max）integer
     *
     * @param min The minimum value
     * @param max The maximum value
     * @return A random integer number from min to max
     */
    public static final int getInt(final int min, final int max) {
        return rnd.getInt(min, max);
    }

    /**
     * Gets a random long number from min(inclusive) to max(inclusive)
     *
     * @param min The minimum value
     * @param max The maximum value
     * @return A random long number from min to max
     */
    public static final long getlong(final long min, final long max) {
        return rnd.getlong(min, max);
    }

    /**
     * 随机数容器 根据RandomType，返回指定的容器---产生调用相应的随机数产生方式
     *
     * @param type
     * @return
     */
    public static final RandomContainer newInstance(final RandomType type) {
        switch (type) {
            case UNSECURE_ATOMIC:
                return new RandomContainer(new Random());

            case UNSECURE_VOLATILE:
                return new RandomContainer(new NonAtomicRandom());

            case UNSECURE_THREAD_LOCAL:
                return new RandomContainer(new ThreadLocalRandom());

            case SECURE:
                return new RandomContainer(new SecureRandom());
        }
        throw new IllegalArgumentException();
    }


    /**
     * ThreadLocalRandom
     *
     * @return
     */
    public static final RandomContainer newInstance() {
        return new RandomContainer(new ThreadLocalRandom());
        // throw new IllegalArgumentException();
    }

    /**
     * Get a random boolean state (true or false)
     *
     * @return A random boolean state (true or false)
     * @see Random#nextBoolean()
     */
    public static final boolean nextBoolean() {
        return rnd.nextBoolean();
    }

    /**
     * Fill the given array with random byte numbers from Byte.MIN_VALUE(inclusive) to Byte.MAX_VALUE(inclusive)
     *
     * @param array The array to be filled with random byte numbers
     * @see Random#nextBytes(byte[] bytes)
     */
    public static final void nextBytes(final byte[] array) {
        rnd.nextBytes(array);
    }

    /**
     * Get a random double number from 0 to 1
     *
     * @return A random double number from 0 to 1
     * @see Random#nextDouble()
     */
    public static final double nextDouble() {
        return rnd.nextDouble();
    }

    /**
     * Get a random float number from 0 to 1
     *
     * @return A random integer number from 0 to 1
     * @see Random#nextFloat()
     */
    public static final float nextFloat() {
        return rnd.nextFloat();
    }

    /**
     * Get a random gaussian double number from 0 to 1
     *
     * @return A random gaussian double number from 0 to 1
     * @see Random#nextGaussian()
     */
    public static final double nextGaussian() {
        return rnd.nextGaussian();
    }

    /**
     * Get a random integer number from Integer.MIN_VALUE(inclusive) to Integer.MAX_VALUE(inclusive)
     *
     * @return A random integer number from Integer.MIN_VALUE to Integer.MAX_VALUE
     * @see Random#nextInt()
     */
    public static final int nextInt() {
        return rnd.nextInt();
    }

    /**
     * @param n
     * @return
     * @see #getInt(int)
     */
    public static final int nextInt(final int n) {
        return getInt(n);
    }

    /**
     * Get a random long number from Long.MIN_VALUE(inclusive) to Long.MAX_VALUE(inclusive)
     *
     * @return A random integer number from Long.MIN_VALUE to Long.MAX_VALUE
     * @see Random#nextLong()
     */
    public static final long nextLong() {
        return rnd.nextLong();
    }

    public static void main(String[] args) {
//        System.out.println(Rnd.getInt(1,900000));
//        System.out.println(Rnd.newInstance(RandomType.UNSECURE_VOLATILE).getDouble());
//        long t1 = System.nanoTime();
//        System.out.println("times:" + (System.nanoTime() - t1));
        System.out.println(Rnd.newInstance().nextFloat());
    }

    /**
     * @author Forsaiken
     */
    public static enum RandomType {
        /**
         * 获取最佳随机值
         *
         * @see SecureRandom
         */
        SECURE,

        /**
         * 获取平均随机值
         *
         * @see Random
         */
        UNSECURE_ATOMIC,

        /**
         * 获取平均随机值
         * 每个线程都有Random对象
         * 提供并行的访问
         *
         * @see ThreadLocalRandom
         */
        UNSECURE_THREAD_LOCAL,

        /**
         * 获取平均随机值
         * 速度更快 ，提供并行的访问
         *
         * @see NonAtomicRandom
         */
        UNSECURE_VOLATILE
    }

    public static final class NonAtomicRandom extends Random {
        private static final long serialVersionUID = 1L;
        private volatile long _seed;

        public NonAtomicRandom() {
            this(++SEED_UNIQUIFIER + System.nanoTime());
        }

        public NonAtomicRandom(final long seed) {
            setSeed(seed);
        }

        @Override
        public final int next(final int bits) {
            return (int) ((_seed = (_seed * MULTIPLIER + ADDEND) & MASK) >>> (48 - bits));
        }

        @Override
        public final void setSeed(final long seed) {
            _seed = (seed ^ MULTIPLIER) & MASK;
        }
    }

    /**
     * @author Forsaiken
     */
    public static final class RandomContainer {
        private final Random _random;

        private RandomContainer(final Random random) {
            _random = random;
        }

        public static void main(String[] args) {
            float f = 0f;
            for (; ; ) {
                f = Rnd.newInstance(RandomType.UNSECURE_THREAD_LOCAL).getInt(1, 6);
                System.out.println(f);
                if (f < 0 || f > 6) {
                    break;
                }
            }
        }

        public final Random directRandom() {
            return _random;
        }

        /**
         * 获取 double值   0 to 1
         *
         * @return A random double number from 0 to 1
         * @see #nextDouble()
         */
        public final double getDouble() {
            return _random.nextDouble();
        }

        /**
         * 获取 integer  值 ；0-n
         *
         * @param n The superior limit (exclusive)
         * @return A random integer number from 0 to n-1
         */
        public final int getInt(final int n) {
            return (int) (_random.nextDouble() * n);
        }

        /**
         * 获取区间随机值（min--max）integer
         * Gets a random integer number from min(inclusive) to max(inclusive)
         *
         * @param min The minimum value
         * @param max The maximum value
         * @return A random integer number from min to max
         */
        public final int getInt(final int min, final int max) {
            return min + (int) (_random.nextDouble() * (max - min + 1));
        }


        /**
         * @param min
         * @param max
         * @return
         */
        public final int getInt(double min, double max) {
            if (min == 0) min = 1;
            if (max == 0) max = 1;
            return (int) (min <= max ? min + (int) (_random.nextDouble() * (max - min + 1))
                    : max + (int) (_random.nextDouble() * (min - max + 1)));
        }

        /**
         * 获取区间随机值（min--max）integer
         * Gets a random integer number from min(inclusive) to max(inclusive)
         *
         * @param min The minimum value
         * @param max The maximum value
         * @return A random integer number from min to max
         */
        public final double getDouble(final double min, final double max) {
            return min + (_random.nextDouble() * (max - min));
        }



        /**
         * 获取区间随机值（min--max）integer
         * Gets a random integer number from min(inclusive) to max(inclusive)
         *
         * @param min The minimum value
         * @param max The maximum value
         * @return A random integer number from min to max
         */
        public final float getFloat(final float min, final float max) {
            return min + (_random.nextFloat() * (max - min));
        }


        /**
         * 获取区间随机值（min--max）long
         *
         * @param min The minimum value
         * @param max The maximum value
         * @return A random long number from min to max
         */
        public final long getlong(final long min, final long max) {
            return min + (long) (_random.nextDouble() * (max - min + 1));
        }

        /**
         * 获取随机 boolean state (true or false)
         *
         * @return A random boolean state (true or false)
         * @see Random#nextBoolean()
         */
        public final boolean nextBoolean() {
            return _random.nextBoolean();
        }

        /**
         * byte--【 Byte.MIN_VALUE(inclusive) to Byte.MAX_VALUE(inclusive)】随机值 填充array
         *
         * @param array The array to be filled with random byte numbers
         * @see Random#nextBytes(byte[] bytes)
         */
        public final void nextBytes(final byte[] array) {
            _random.nextBytes(array);
        }

        /**
         * 获取随机的 double 值 from 0 to 1
         *
         * @return A random double number from 0 to 1
         * @see Random#nextDouble()
         */
        public final double nextDouble() {
            return _random.nextDouble();
        }

        /**
         * 获取随机的 float 值  number from 0 to 1
         *
         * @return A random integer number from 0 to 1
         * @see Random#nextFloat()
         */
        public final float nextFloat() {
            return _random.nextFloat();
        }

        /**
         * 获取随机的 gaussian double 值  number from 0 to 1
         *
         * @return A random gaussian double number from 0 to 1
         * @see Random#nextGaussian()
         */
        public final double nextGaussian() {
            return _random.nextGaussian();
        }

        /**
         * Get a random integer number from Integer.MIN_VALUE(inclusive) to Integer.MAX_VALUE(inclusive)
         *
         * @return A random integer number from Integer.MIN_VALUE to Integer.MAX_VALUE
         * @see Random#nextInt()
         */
        public final int nextInt() {
            return _random.nextInt();
        }

        /**
         * Get a random long number from Long.MIN_VALUE(inclusive) to Long.MAX_VALUE(inclusive)
         *
         * @return A random integer number from Long.MIN_VALUE to Long.MAX_VALUE
         * @see Random#nextLong()
         */
        public final long nextLong() {
            return _random.nextLong();
        }
    }

    /**
     * This class extends {@link Random} but do not compare and store atomically.<br>
     * Instead it`s using thread local ensure reading and storing the whole 64bit seed chunk.<br>
     * This implementation is the fastest, never generates the same seed for 2 threads.<br>
     * Each thread has it`s own random instance.
     *
     * @author Forsaiken
     * @see Random
     */
    public static final class ThreadLocalRandom extends Random {
        private static final long serialVersionUID = 1L;
        private final ThreadLocal<Seed> _seedLocal;

        public ThreadLocalRandom() {
            _seedLocal = new ThreadLocal<Seed>() {
                @Override
                public final Seed initialValue() {
                    return new Seed(++SEED_UNIQUIFIER + System.nanoTime());
                }
            };
        }

        public ThreadLocalRandom(final long seed) {
            _seedLocal = new ThreadLocal<Seed>() {
                @Override
                public final Seed initialValue() {
                    return new Seed(seed);
                }
            };
        }

        @Override
        public final int next(final int bits) {
            return _seedLocal.get().next(bits);
        }

        @Override
        public final void setSeed(final long seed) {
            if (_seedLocal != null)
                _seedLocal.get().setSeed(seed);
        }

        private static final class Seed {
            long _seed;

            Seed(final long seed) {
                setSeed(seed);
            }

            final int next(final int bits) {
                return (int) ((_seed = (_seed * MULTIPLIER + ADDEND) & MASK) >>> (48 - bits));
            }

            final void setSeed(final long seed) {
                _seed = (seed ^ MULTIPLIER) & MASK;
            }
        }
    }
}
